=begin pod

=TITLE class IO::Path

=SUBTITLE File or directory path

=for code
class IO::Path is Cool does IO { }

The workhorse of IO operations.

Conceptually, an C<IO::Path> object consists of a volume, a directory, and a
basename. It supports both purely textual operations, and operations that access
the file system, e.g. to resolve a path, or to read all content of a file.

At creation, each C<IO::Path> object is given information about the current
working directory the path might be relative to using the C<$.CWD> attribute
(defaults to L«C<$*CWD>|/language/variables#Dynamic_variables»), as well as
what operating system semantics should be used for path manipulation using
the special L«C<IO::Spec>|/type/IO::Spec» type given in the C<$.SPEC> attribute.

The C<$.SPEC> defaults to the value of
L«C<$*SPEC>|/language/variables#Dynamic_variables», which uses the object
suitable for the operating system the code is currently running on. This is
the default most code will be comfortable with.

In certain situations, e.g. testing, you may wish to force C<$*SPEC> to use one
of the specific SPEC modules: L«C<IO::Spec::Unix>|/type/IO::Spec::Unix»,
L«C<IO::Spec::Win32>|/type/IO::Spec::Win32»,
L«C<IO::Spec::Cygwin>|/type/IO::Spec::Cygwin», and
L«C<IO::Spec::QNX>|/type/IO::Spec::QNX», or to create C<IO::Path> objects via
shortcut subclasses L«C<IO::Path::Unix>|/type/IO::Path::Unix»,
L«C<IO::Path::Win32>|/type/IO::Path::Win32»,
L«C<IO::Path::Cygwin>|/type/IO::Path::Cygwin», and
L«C<IO::Path::QNX>|/type/IO::Path::QNX» that pre-set the C<$.SPEC> attribute
for you.

The rest of this document silently assumes Unix semantics in its examples,
unless stated otherwise.

=head1 Methods

=head2 method new

Defined as:

=for code :skip-test
multi method new(Str:D $path, IO::Spec :$SPEC = $*SPEC, Str() :$CWD = $*CWD)
multi method new(
    :$basename!, :$dirname = '.', :$volume = ''
    IO::Spec :$SPEC = $*SPEC, Str() :$CWD = $*CWD
)

Creates a new C<IO::Path> object from a path string (which is being parsed for
volume, directory name and basename), or from volume, directory name and
basename passed as named arguments.

The path's operation will be performed using C<:$SPEC> semantics (defaults to
current L«C<$*SPEC>|/language/variables#Dynamic_variables») and will use
C<:$CWD> as the directory the path is relative to (defaults to
L«C<$*CWD>|/language/variables#Dynamic_variables»).

=head2 attribute CWD

    IO::Path.new("foo", :CWD</home/camelia>)
        .IO.CWD.say; # OUTPUT: «/home/camelia␤»

Read-only. Contains implicit or explicit value of C<:$CWD> argument to C<.new>.

=head2 attribute SPEC

    IO::Path.new("foo", :SPEC(IO::Spec::Unix.new))\
        .IO.SPEC.^name.say; # OUTPUT: «IO::Spec::Unix␤»

Read-only. Contains implicit or explicit value of C<:$SPEC> argument to C<.new>.

=head2 attribute path

    IO::Path.new("foo").path.say; # OUTPUT: «foo␤»

Read-only. Returns the string the object was constructed from or the value of
C<$SPEC.join($volume, $dirname, $basename)> if multi-part version of C<.new>
was used. B<NOTE:> this does not include the C<$.CWD>; see
L«C<IO::Path.absolute>|/routine/absolute»
and L«C<IO::Path.relative>|/routine/relative» for
stringification options that include C<$.CWD>.

B<NOTE:> Implementations may cache operations done with this attribute, so
modifying its value (via cloning or Proxy) is NOT recommended and may result
in broken C<IO::Path> objects. Create a new C<IO::Path> object instead.

=head2 method ACCEPTS

Defined as:

    multi method ACCEPTS(IO::Path:D: Cool:D $other --> Bool:D)

Coerces the argument to C<IO::Path>, if necessary. Returns C<True> if
L«C<.absolute>|/routine/absolute» method on both paths returns the same string.
B<NOTE:> it's possible for two paths that superficially point to the same
resource to NOT smartmatch as C<True>, if they were constructed differently and
were never fully resolved:

    say "foo/../bar".IO ~~ "bar".IO # False

The reason is the two paths above may point to different resources when fully
resolved (e.g. if C<foo> is a symlink). Resolve the paths before smartmatching
to check they point to same resource:

    say "foo/../bar".IO.resolve(:completely) ~~ "bar".IO.resolve(:completely) # True

=head2 method basename

Defined as:

    method basename(IO::Path:D:)

Returns the basename part of the path object, which is the name of the
filesystem object itself that is referenced by the path.

    "docs/README.pod".IO.basename.say; # OUTPUT: «README.pod␤»
    "/tmp/".IO.basename.say;           # OUTPUT: «tmp␤»

Note that in L«C<IO::Spec::Win32>|/type/IO::Spec::Win32» semantics, the
C<basename> of a Windows share is C<\>, not the name of the share itself:

    IO::Path::Win32.new('//server/share').basename.say; # OUTPUT: «\␤»

=head2 method add

Defined as:

    method add(IO::Path:D: Str() $what --> IO::Path:D)

Concatenates a path fragment to the invocant and returns the resultant
C<IO::Path>. If adding C<../> to paths that end with a file, you may
need to call L<resolve> for the resultant path to be accessible by other
C<IO::Path> methods like L<dir> or L<open>. See also L<sibling> and
L<parent>.

    "foo/bar".IO.mkdir;
    "foo/bar".IO.add("meow")    .resolve.relative.say; # OUTPUT: «foo/bar/meow␤»
    "foo/bar".IO.add("/meow")   .resolve.relative.say; # OUTPUT: «foo/bar/meow␤»
    "foo/bar".IO.add("meow.txt").resolve.relative.say; # OUTPUT: «foo/bar/meow.txt␤»
    "foo/bar".IO.add("../meow") .resolve.relative.say; # OUTPUT: «foo/meow␤»
    "foo/bar".IO.add("../../")  .resolve.relative.say; # OUTPUT: «.␤»

=head2 method child

Defined as:

    method child(IO::Path:D: Str() $childname --> IO::Path:D)

Alias for L«C<.add>|/routine/add». B<NOTE:> C<.child> will be switched to
secure version around 6.e release time, which will C<fail()> with non-child
paths and is also slower, since it has to do more work. Use
L«C<.add>|/routine/add» if your code does not need to guarantee the added
path is in fact a child path.

=head2 method cleanup

Defined as:

    method cleanup(IO::Path:D: --> IO::Path:D)

Returns a new path that is a canonical representation of the invocant path,
cleaning up any extraneous path parts:

    "foo/./././..////bar".IO.cleanup.say;      # OUTPUT: «"foo/../bar".IO␤»
    IO::Path::Win32.new("foo/./././..////bar")
        .cleanup.say; "foo\..\bar".IO;         # OUTPUT: «"foo\..\bar".IO␤»

Note that no file system access is made. See also
L«C<resolve>|/routine/resolve».

=head2 method comb

Defined as:

    method comb(IO::Path:D: |args --> Seq:D)

Opens the file and processes its contents the same way
L«C<Str.comb>|/type/Str#routine_comb» does, taking the same arguments.
Implementations may slurp the file in its entirety when this method is called.

=head2 method split

Defined as:

    method split(IO::Path:D: |args --> Seq:D)

Opens the file and processes its contents the same way
L«C<Str.split>|/type/Str#routine_split» does, taking the same arguments.
Implementations may slurp the file in its entirety when this method is called.

=head2 method extension

Defined as:

    multi method extension(IO::Path:D:                                         --> Str:D)
    multi method extension(IO::Path:D:               Int :$parts               --> Str:D)
    multi method extension(IO::Path:D:             Range :$parts               --> Str:D)
    multi method extension(IO::Path:D: Str $subst,   Int :$parts, Str :$joiner --> IO::Path:D)
    multi method extension(IO::Path:D: Str $subst, Range :$parts, Str :$joiner --> IO::Path:D)

Returns the extension consisting of C<$parts> parts (defaults to C<1>),
where a "part" is defined
as a dot followed by possibly-empty string up to the end of the string, or
previous part. That is C<"foo.tar.gz"> has an extension of two parts: first part
is C<"gz"> and second part is C<"tar"> and calling
C<"foo.tar.gz".IO.extension: :2parts> gives C<"tar.gz">. If an extension with
the specified number of C<$parts> is not found, returns an empty string.

C<$parts> can be a L«C<Range>|/type/Range», specifying the minimum number of
parts and maximum number of parts the extension should have. The routine will
attempt to much the most parts it can. If C<$parts> range's endpoints that are
smaller than C<0> they'll be treated as C<0>; implementations may treat
endpoints larger than C<2⁶³-1> as C<2⁶³-1>. Ranges with C<NaN> or
L«C<Str>|/type/Str» endpoints will cause an exception to be thrown.

If C<$subst> is provided, the extension will be instead replaced with C<$subst>
and a new C<IO::Path> object will be returned. It will be joined to the file's
name with C<$joiner>, which defaults to an empty string when C<$subst> is
an empty string and to C<"."> when C<$subst> is not empty. B<Note:> if as
the result of replacement the L«C<basename>|/routine/basename» of the path
ends up being empty, it will be assumed to be C<.> (a single dot).

    # Getting an extension:
    say "foo.tar.gz".IO.extension;               # OUTPUT: «gz␤»
    say "foo.tar.gz".IO.extension: :2parts;      # OUTPUT: «tar.gz␤»
    say "foo.tar.gz".IO.extension: :parts(^5);   # OUTPUT: «tar.gz␤»
    say "foo.tar.gz".IO.extension: :parts(0..1); # OUTPUT: «gz␤»

    # Replacing an extension
    say "foo.tar.gz".IO.extension: '';                # OUTPUT: «"foo.tar".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP';             # OUTPUT: «"foo.tar.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :0parts;    # OUTPUT: «"foo.tar.gz.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :2parts;    # OUTPUT: «"foo.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :parts(^5); # OUTPUT: «"foo.ZIP".IO␤»

    # Replacing an extension using non-standard joiner:
    say "foo.tar.gz".IO.extension: '',    :joiner<_>;  # OUTPUT: «"foo.tar_".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>;  # OUTPUT: «"foo.tar_ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>,
                                           :2parts;     # OUTPUT: «"foo_ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>,
                                           :parts(^5);  # OUTPUT: «"foo_ZIP".IO␤»

    # EDGE CASES:

    # There is no 5-part extension, so returned value is an empty string
    say "foo.tar.gz".IO.extension: :5parts; # OUTPUT: «␤»

    # There is no 5-part extension, so we replaced nothing:
    say "foo.tar.gz".IO.extension: 'ZIP', :5parts; # OUTPUT: «"foo.tar.gz".IO␤»

    # Replacing a 0-part extension is just appending:
    say "foo.tar.gz".IO.extension: 'ZIP', :0parts; # OUTPUT: «"foo.tar.gz.ZIP".IO␤»

    # Replace 1-part of the extension, using '.' joiner
    say "...".IO.extension: 'tar'; # OUTPUT: «"...tar".IO␤»

    # Replace 1-part of the extension, using empty string joiner
    say "...".IO.extension: 'tar', :joiner(''); # OUTPUT: «"..tar".IO␤»

    # Remove 1-part extension; results in empty basename, so result is ".".IO
    say ".".IO.extension: ''; # OUTPUT: «".".IO␤»

=head2 method dirname

Defined as:

    method dirname(IO::Path:D:)

Returns the directory name portion of the path object. That is, it returns
the path excluding the L<volume|/routine/volume> and the
L<base name|/routine/basename>.

    say IO::Path.new("/home/camelia/myfile.p6").dirname; # OUTPUT: «/home/camelia␤»
    say IO::Path::Win32.new("C:/home/camelia").dirname;  # OUTPUT: «/home␤»
    say IO::Path.new("/home").dirname;                   # OUTPUT: «/␤»

=head2 method volume

Defined as:

    method volume(IO::Path:D:)

Returns the volume portion of the path object. On Unix system, this is always
the empty string.

    say IO::Path::Win32.new("C:\\Windows\\registry.ini").volume;    # OUTPUT: «C:␤»

=head2 method parts

Defined as:

    method parts(IO::Path:D: --> Map:D)

Returns a L<Map> with the keys C<volume>, C<dirname>, C<basename> whose values
are the same as available via methods L«C<.volume>|/routine/volume»,
L«C<.dirname>|/routine/dirname», and L«C<.basename>|/routine/basename»
respectively.

    say IO::Path::Win32.new("C:/rakudo/perl6.bat").parts.perl;
    # OUTPUT: «Map.new((:basename("perl6.bat"),:dirname("/rakudo"),:volume("C:")))␤»

=head2 method perl

Defined as:

    method perl(IO::Path:D: --> Str:D)

Returns a string that, when given passed through L«C<EVAL>|/routine/EVAL»
gives the original invocant back.

    "foo/bar".IO.perl.say;
    # OUTPUT: IO::Path.new("foo/bar", :SPEC(IO::Spec::Unix), :CWD("/home/camelia"))

Note that this string includes the value of the C<.CWD> attribute that is set
to L«C<$*CWD>|/language/variables#Dynamic_variables» when the path
object was created, by default.

=head2 method gist

Defined as:

    method gist(IO::Path:D: --> Str:D)

Returns a string, part of which contains either the value of
L«C<.absolute>|/type/IO::Path#method_absolute» (if path is absolute) or
L«C<.path>|/type/IO::Path#attribute_path». Note that no escaping
of special characters is made, so e.g. C<"\b"> means a path contains a backslash
and letter "b", not a backspace.

    say "foo/bar".IO;                       # OUTPUT: «"foo/bar".IO␤»
    say IO::Path::Win32.new: ｢C:\foo/bar\｣; # OUTPUT: «"C:\foo/bar\".IO␤»

=head2 method Str

Defined as:

    method Str(IO::Path:D: --> Str)

Alias for L«C<IO::Path.path>|/routine/path». In particular, note that default
stringification of an C<IO::Path> does B<NOT> use the value of
L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD». To stringify while
retaining full path information use L«C<.absolute>|/routine/absolute» or
L«C<.relative>|/routine/relative» methods.

=head2 method succ

Defined as:

    method succ(IO::Path:D: --> IO::Path:D)

Returns a new L<IO::Path> constructed from the invocant, with
L«C<.basename>|/routine/basename» changed by calling
L«C<Str.succ>|/type/Str#method_succ» on it.

    "foo/file02.txt".IO.succ.say; # OUTPUT: «"foo/file03.txt".IO␤»

=head2 method open

Defined as:

    method open(IO::Path:D: *%opts)

Opens the path as a file; the named options control the mode, and are the
same as the L<open> function accepts.

=head2 method pred

Defined as:

    method pred(IO::Path:D: --> IO::Path:D)

Returns a new L<IO::Path> constructed from the invocant, with
L«C<.basename>|/routine/basename» changed by calling
L«C<Str.pred>|/type/Str#method_pred» on it.

    "foo/file02.txt".IO.pred.say; # OUTPUT: «"foo/file01.txt".IO␤»

=head2 method watch

Defined as:

    method watch(IO::Path:D: --> Supply:D)

Equivalent to calling
L«IO::Notification.watch-path|/type/IO::Notification#method_watch-path»
with the invocant as the argument.

=head2 method is-absolute

Defined as:

    method is-absolute(IO::Path:D: --> Bool)

Returns C<True> if the path is an absolute path, and C<False> otherwise.

    "/foo".IO.is-absolute.say; # OUTPUT: «True␤»
    "bars".IO.is-absolute.say; # OUTPUT: «False␤»

Note that on Windows a path that starts with a slash or backslash is still
considered absolute even if no volume was given, as it is absolute for that
particular volume:

    IO::Path::Win32.new("/foo"  ).is-absolute.say; # OUTPUT: «True␤»
    IO::Path::Win32.new("C:/foo").is-absolute.say; # OUTPUT: «True␤»
    IO::Path::Win32.new("C:foo" ).is-absolute.say; # OUTPUT: «False␤»

=head2 method is-relative

Defined as:

    method is-relative(IO::Path:D: --> Bool)

Returns C<True> if the path is a relative path, and C<False> otherwise.
Windows caveats for L«C<.is-absolute>|/type/IO::Path#method_is-absolute»
apply.

=head2 method absolute

Defined as:

    multi method absolute(IO::Path:D: --> Str)
    multi method absolute(IO::Path:D: $base --> Str)

Returns a new C<Str> object that is an absolute path. If the invocant
is not already an absolute path, it is first made absolute using C<$base>
as base, if it is provided, or the C<.CWD> attribute the object was
created with if it is not.

=head2 method relative

Defined as:

    method relative(IO::Path:D: $base = $*CWD --> Str)

Returns a new C<Str> object with the path relative to the C<$base>. If C<$base>
is not provided, C<$*CWD> is used in its place. If the invocant is not
an absolute path, it's first made to be absolute using the C<.CWD>
attribute the object was created with, and then is made relative to C<$base>.

=head2 method parent

Defined as:

    method parent(IO::Path:D: --> IO::Path:D)

Returns the parent path of the invocant. Note that no file system access
is made, so the returned parent is physical and not the logical parent of
symlinked directories.

    '/etc/foo'.IO.parent.say; # OUTPUT: «"/etc".IO␤»
    '/etc/..' .IO.parent.say; # OUTPUT: «"/etc".IO␤»
    '/etc/../'.IO.parent.say; # OUTPUT: «"/etc".IO␤»
    './'      .IO.parent.say; # OUTPUT: «"..".IO␤»
    'foo'     .IO.parent.say; # OUTPUT: «".".IO␤»
    '/'       .IO.parent.say; # OUTPUT: «"/".IO␤»
    IO::Path::Win32.new('C:/').parent.say; # OUTPUT: «"C:/".IO␤»

=head2 method resolve

Defined as:

    method resolve(IO::Path:D: :$completely --> IO::Path)

Returns a new C<IO::Path> object with all symbolic links and references to the
parent directory (C<..>) resolved. This means that the filesystem is examined
for each directory in the path, and any symlinks found are followed.

    # bar is a symlink pointing to "/baz"
    my $io = "foo/./bar/..".IO.resolve;      # now "/" (the parent of "/baz")

If C<:$completely>, which defaults to C<False>, is set to a true value, the
method will L«C<fail>|/routine/fail» with C<X::IO::Resolve>
if it cannot completely resolve the path,
otherwise, it will resolve as much as possible, and will merely perform
L«C<cleanup>|/routine/cleanup» of the rest of the path. The last part of the
path does B<NOT> have to exist to C<:$completely> resolve the path.

NOTE: Currently (April 2017) this method doesn't work correctly on all
platforms, e.g. Windows, since it assumes POSIX semantics.

=head2 routine dir

Defined as:

    sub    dir(Cool $path = '.', Mu :$test = none('.', '..'))
    method dir(IO::Path:D: Mu :$test = none('.', '..'))

Returns the contents of a directory as a lazy list of C<IO::Path> objects
representing relative paths, filtered by L<smart-matching|
/language/operators#infix_~~> their names (as strings) against the C<:test>
parameter.

B<NOTE:> a C<dir> call opens a directory for reading, which counts towards
maximum per-process open files for your program. Be sure to exhaust returned
L<Seq> before doing something like recursively performing more C<dir> calls.
You can exhaust it by assigning to a C<@->sigiled variable or simply looping
over it. Note how examples below push further dirs to look through into
an L<Array>, rather than immediately calling C<dir> on them. See also
L«C<IO::Dir> module|https://modules.perl6.org/dist/IO::Dir» that gives you finer
control over closing dir handles.

Examples:

    # To iterate over the contents of the current directory:
    for dir() -> $file {
        say $file;
    }

    # As before, but include even '.' and '..' which are filtered out by
    # the default :test matcher:
    for dir(test => *) -> $file {
        say $file;
    }

    # To get the names of all .jpg and .jpeg files in ~/Downloads:
    my @jpegs = $*HOME.dir: test => /:i '.' jpe?g $/;

=comment TODO: Turn the following into a FAQ entry, and link to it from here.

An example program that lists all files and directories recursively:

    sub MAIN($dir = '.') {
        my @todo = $dir.IO;
        while @todo {
            for @todo.pop.dir -> $path {
                say $path.Str;
                @todo.push: $path if $path.d;
            }
        }
    }

A lazy way to find the first three files ending in ".p6" recursively starting from the current directory:

=for code
my @stack = '.'.IO;
my $perl-files = gather while @stack {
    with @stack.pop {
        when :d { @stack.append: .dir }
        .take when .extension.lc eq 'p6'
    }
}
.put for $perl-files[^3];

=head2 File test operators

For most file tests, you can do a smart match C<~~> or you can call a method.
You don't need to actually open a filehandle in the traditional way (although
you can) to do a filetest. You can simply append C<.IO> to the filename. For
instance, here is how to check if a file is readable using smart match:
C<'/path/to/file'.IO ~~ :r>

You can, of course, use an already opened filehandle. Here, using the file
handle C<$fh>, is an example, using the method syntax for the file test:
    $fh.r

File tests include
    :e L«Exists|/type/IO::Path#method_e»
    :d L«Directory|/type/IO::Path#method_d»
    :f L«File|/type/IO::Path#method_f»
    :l L«Symbolic link|/type/IO::Path#method_l»
    :r L«Readable|/type/IO::Path#method_r»
    :w L«Writable|/type/IO::Path#method_w»
    :x L«Executable|/type/IO::Path#method_x»
    :s L«Size|/type/IO::Path#method_s»
    :z L«Zero size|/type/IO::Path#method_z»

All of these tests can be used as methods (without the colon).
Three tests, however exist only as methods:
    $fh.modified;
    $fh.accessed;
    $fh.changed;

=head2 method e

Defined as:

    method e(--> Bool:D)

Returns C<True> if the invocant is a path that exists.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method d

Defined as:

    method d(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is a directory.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method f

Defined as:

    method f(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is a file.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method s

Defined as:

    method s(--> Int:D)

Returns the file size in bytes. May be called on paths that are directories, in
which case the reported size is dependent on the operating system.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method l

Defined as:

    method l(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is a symlink.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method r

Defined as:

    method r(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is accessible.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method w

Defined as:

    method w(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is writable.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method rw

Defined as:

    method rw(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is readable and
writable. The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist>
if the path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method x

Defined as:

    method x(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is executable.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method rwx

Defined as:

    method rwx(--> Bool:D)

Returns C<True> if the invocant is a path that exists and is executable,
readable, and writable. The method will L«C<fail>|/routine/fail» with
C<X::IO::DoesNotExist> if the path points to a non-existent filesystem entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method z

Defined as:

    method z(--> Bool:D)

Returns C<True> if the invocant is a path that exists and has size of C<0>.
May be called on paths that are directories, in
which case the reported file size (and thus the result of this method)
is dependent on the operating system. The method will L«C<fail>|/routine/fail»
with C<X::IO::DoesNotExist> if the path points to a non-existent filesystem
entity.

TIP: use L«smartmatch with Pairs|/type/Pair#method_ACCEPTS» to perform multiple
file tests.

=head2 method sibling

Defined as:

    method sibling(IO::Path:D: Str() $sibling --> IO::Path:D)

Allows to reference a sibling file or directory. Returns a new
L«C<IO::Path>|/type/IO::Path» based on the invocant, with the
L«C<.basename>|/type/IO::Path#method_basename» changed to C<$sibling>. The
C<$sibling> is allowed to be a multi-part path fragment; see also
L«C<.add>|/type/IO::Path#method_add».

    say '.bashrc'.IO.sibling: '.bash_aliases'; # OUTPUT: «.bash_aliases".IO␤»
    say '/home/camelia/.bashrc'.IO.sibling: '.bash_aliases';
    # OUTPUT: «/home/camelia/.bash_aliases".IO␤»

    say '/foo/' .IO.sibling: 'bar';  # OUTPUT: «/bar".IO␤»
    say '/foo/.'.IO.sibling: 'bar';  # OUTPUT: «/foo/bar".IO␤»

=head2 method words

Defined as:

    method words(IO::Path:D: :$chomp = True, :$enc = 'utf8', :$nl-in = ["\x0A", "\r\n"], |c --> Seq:D)

Opens the invocant and returns its L«words|/type/IO::Handle#routine_words».

The behaviour is equivalent to L<opening|/routine/open> the file specified by
the invocant, forwarding the C<:$chomp>, C<:$enc>, and C<:$nl-in> arguments to
L«C<IO::Handle.open>|/type/IO::Handle#routine_open», then calling
L«C<IO::Handle.words>|/type/IO::Handle#routine_words» on that handle, forwarding
any of the remaining arguments to that method, and returning the resultant
L<Seq>.

B<NOTE:> the words are ready lazily and the handle used under the hood won't
get closed until the returned L<Seq> is
L<fully reified|/language/glossary#index-entry-Reify>, so ensure it is,
or you'll be leaking open file handles. (TIP: use the
L«C<$limit> argument|/type/IO::Handle#routine_words»)

=for code
my %dict := bag 'my-file.txt'.IO.words;
say "Most common words: ", %dict.sort(-*.value).head: 5;

=head2 method lines

Defined as:

    method lines(IO::Path:D: :$chomp = True, :$enc = 'utf8', :$nl-in = ["\x0A", "\r\n"], |c --> Seq:D)

Opens the invocant and returns its L«lines|/type/IO::Handle#routine_lines».

The behaviour is equivalent to L<opening|/routine/open> the file specified by
the invocant, forwarding the C<:$chomp>, C<:$enc>, and C<:$nl-in> arguments to
L«C<IO::Handle.open>|/type/IO::Handle#routine_open», then calling
L«C<IO::Handle.lines>|/type/IO::Handle#routine_lines» on that handle, forwarding
any of the remaining arguments to that method, and returning the resultant
L<Seq>.

B<NOTE:> the lines are ready lazily and the handle used under the hood won't
get closed until the returned L<Seq> is
L<fully reified|/language/glossary#index-entry-Reify>, so ensure it is,
or you'll be leaking open file handles. (TIP: use the
L«C<$limit> argument|/type/IO::Handle#routine_lines»)

=begin code
say "The file contains ",
  '50GB-file'.IO.lines.grep(*.contains: 'Perl').elems,
  " lines that mention Perl";
# OUTPUT: «The file contains 72 lines that mention Perl␤»
=end code

=head2 routine slurp

Defined as:

    multi method slurp(IO::Path:D: :$bin, :$enc)

Read all of the file's content and return it as either L<Buf>, if C<:$bin>
is C<True>, or if not, as L<Str> decoded with C<:$enc> encoding, which defaults
to C<utf8>. See L«C<&open>|/routine/open» for valid values for C<:$enc>.

=head2 method spurt

Defined as:

    method spurt(IO::Path:D: $data, :$enc, :$append, :$createonly)

Opens the file path for writing, and writes all of the C<$data> into it.
Will L«C<fail>|/routine/fail» if it cannot succeed for any reason.
The C<$data> can be any L«C<Cool>|/type/Cool» type or any L«C<Blob>|/type/Blob»
type. Arguments are as follows:

=item C<:$enc> — character encoding of the data. Takes same values as C<:$enc>
in L«C<IO::Handle.open>|/routine/open». Defaults to C<utf8>. Ignored if C<$data>
is a L«C<Blob>|/type/Blob».

=item C<:$append> — open the file in C<append> mode, preserving existing
contents, and appending data to the end of the file.

=item C<:$createonly> — L«C<fail>|/routine/fail» if the file already exists.

=head2 method chdir

Defined as:

    multi method chdir(IO::Path:D: Str() $path, :$d = True, :$r, :$w, :$x)

B<DEPRECATION NOTICE:> this method will be deprecated in C<6.d> language
and removed in C<6.e>. Do
not use it for new code. Instead, create a new path or use
L«C<add>|/routine/add» method. For altering current working
directory see L«C<&chdir>|/routine/chdir» and L«C<&*chdir>|/routine/&*chdir»
subroutines.

Contrary to the name, the C<.chdir> method does not change any directories,
but merely concatenates the given C<$path> to the invocant and returns the
resultant C<IO::Path>. Optional file tests can be performed by providing
C<:d>, C<:r>, C<:w>, or C<:x> L«C<Bool>|/type/Bool» named arguments; when
set to C<True>, they'll perform L«C<.d>|/routine/d», L«C<.r>|/routine/r»,
L«C<.w>|/routine/w», and L«C<.x>|/routine/x» tests respectively. By default,
only C<:d> is set to C<True>.

=head2 routine mkdir

Defined as:

    method mkdir(IO::Path:D: Int() $mode = 0o777 --> IO::Path:D)
    sub mkdir(   IO() $path, Int() $mode = 0o777 --> IO::Path:D)

Creates a new directory. See L«C<mode>|/routine/mode» for explanation and
valid values for C<$mode>. Returns the L<IO::Path> object pointing to
the newly created directory on success;
L<fails|/routine/fail> with L<X::IO::Mkdir> if directory cannot be created.

Creates parent directories, as needed (similar to *nix utility C<mkdir> with C<-p> option).
That is, C<mkdir "foo/bar/ber/meow"> will create C<foo>, C<foo/bar>, and C<foo/bar/ber>
directories if they do not exist, as well as create C<foo/bar/ber/meow>.

=head2 routine rmdir

Defined as:

    sub    rmdir(*@dirs --> List:D)
    method rmdir(IO::Path:D: --> True)

Remove the invocant, or in sub form, all of the provided directories in the
given list, which can contain any L<Cool> object. Only works on empty
directories.

Method form returns C<True> on success and throws an exception of type
C<X::IO::Rmdir> if the directory cannot be removed (e.g.
the directory is not empty, or the path is not a directory). Subroutine
form returns a list of directories that were successfully deleted.

To delete non-empty directory, see L«rmtree in C<File::Directory::Tree>
module|https://github.com/labster/p6-file-directory-tree».

=head2 method chmod

Defined as:

    method chmod(IO::Path:D: Int() $mode --> Bool)

Changes the POSIX permissions of a file or directory to C<$mode>.
Returns C<True> on success; on failure,
L<fails|/routine/fail> with L<X::IO::Chmod>.

The mode is expected as an integer following the L<standard numeric notation|
https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation>, and is
best written as an octal number:

=for code
'myfile'.IO.chmod(0o444);          # make a file read-only
'somedir'.IO.chmod(0o777);         # set 0777 permissions on a directory

Make sure you I<don't> accidentally pass the intended octal digits as a decimal
number (or string containing a decimal number):

=for code
'myfile'.IO.chmod:  '0444';        # BAD!!! (interpreted as mode 0o674)
'myfile'.IO.chmod: '0o444';        # OK (an octal in a string)
'myfile'.IO.chmod:  0o444;         # Also OK (an octal literal)

=head2 routine rename

Defined as:

    method rename(IO::Path:D: IO() $to, :$createonly = False --> Bool:D)
    sub    rename(IO() $from, IO() $to, :$createonly = False --> Bool:D)

Renames a file or directory. Returns C<True> on success; L<fails|/routine/fail>
with L<X::IO::Rename> if C<:$createonly> is C<True> and the C<$to> path already
exists or if the operation failed for some other reason.

B<Note:> some renames will always fail, such as when the new name is on a
different storage device. See also: L«C<move>|/routine/move».

=head2 routine copy

Defined as:

    method copy(IO::Path:D: IO() $to, :$createonly --> Bool:D)
    sub    copy(IO() $from, IO() $to, :$createonly --> Bool:D)

Copies a file. Returns C<True> on success; L<fails|/routine/fail>
with L<X::IO::Copy> if C<:$createonly> is C<True> and the C<$to> path already
exists or if the operation failed for some other reason, such as when
C<$to> and C<$from> are the same file.

=head2 routine move

Defined as:

    method move(IO::Path:D: IO() $to, :$createonly --> Bool:D)
    sub    move(IO() $from, IO() $to, :$createonly --> Bool:D)

Copies a file and then removes the original. If removal fails, it's possible
to end up with two copies of the file. Returns C<True> on success;
L<fails|/routine/fail> with L<X::IO::Move> if C<:$createonly> is C<True> and
the C<$to> path already exists or if the operation failed for some other reason,
such as when C<$to> and C<$from> are the same file.

To avoid copying, you can use L«C<rename>|/routine/rename», if the files are on
the same storage device. It also works with directories, while C<move> does not.

=head2 method Numeric

Defined as:

    method Numeric(IO::Path:D: --> Numeric:D)

Coerces L«C<.basename>|/routine/basename» to L<Numeric>. L<Fails|/routine/fail>
with C<X::Str::Numeric> if base name is not numerical.

=head2 method Int

Defined as:

    method Int(IO::Path:D: --> Int:D)

Coerces L«C<.basename>|/routine/basename» to L<Int>. L<Fails|/routine/fail>
with C<X::Str::Numeric> if base name is not numerical.

=head2 routine symlink

Defined as:

    method symlink(IO::Path:D $target: IO() $link --> Bool:D)
    sub    symlink(      IO() $target, IO() $link --> Bool:D)

Create a new I<symbolic> link C<$link> to existing C<$target>.
Returns C<True> on success; L<fails|/routine/fail> with
L<X::IO::Symlink> if the symbolic link could not be created. If C<$target>
does not exist, creates a dangling symbolic link.
To create a hard link, see L«C<link>|/routine/link».

B<Note:> on Windows, creation of symbolic links may require escalated
privileges.

=head2 routine link

Defined as:

    method link(IO::Path:D $target: IO() $link --> Bool:D)
    sub    link(      IO() $target, IO() $link --> Bool:D)

Create a new I<hard> link C<$link> to existing C<$target>.
Returns C<True> on success; L<fails|/routine/fail> with
L<X::IO::Link> if the hard link could not be created.
To create a symbolic link, see L«C<symlink>|/routine/symlink».

=head2 routine unlink

Defined as:

    method unlink(IO::Path:D: --> True)
    sub    unlink(*@filenames --> List:D)

Delete all specified ordinary files, links, or symbolic links. See L<rmdir>
to delete directories.

The subroutine form returns the names of the files that were successfully
deleted. The method form returns C<True> on success, or
L<fails|/routine/fail> with L<X::IO::Unlink> if the operation could not be
completed.

=head2 method IO

Defined as:

    method IO(IO::Path:D: --> IO::Path)

Returns the invocant.

=head2 method SPEC

Defined as:

    method SPEC(IO::Path:D: --> IO::Spec)

Returns the L<IO::Spec|/type/IO::Spec> object that was (implicitly) specified at object
creation time.

    my $io = IO::Path.new("/bin/bash");
    say $io.SPEC;                            # OUTPUT: «(Unix)␤»
    say $io.SPEC.dir-sep;                    # OUTPUT: «/␤»

=head1 File timestamp retrieval

There are also 3 methods for fetching the 3 timestamps of a file (inode),
on Operating Systems where these are available:

=head2 method modified

Returns an L«C<Instant>|/type/Instant» object indicating when the file was
last modified.

=for code
say "path/to/file".IO.modified;          # Instant:1424089165
say "path/to/file".IO.modified.DateTime; # 2015-02-16T12:18:50Z

=head2 method accessed

Return an L<Instant> object representing the timestamp when the file was
last accessed. B<Note:> depending on how the filesystem was mounted, the
last accessed time may not update on I<each access> to the file, but only
on the first access after modifications.

=for code
say "path/to/file".IO.accessed;          # Instant:1424353577
say "path/to/file".IO.accessed.DateTime; # 2015-02-19T13:45:42Z

=head2 method changed

Returns an L«C<Instant>|/type/Instant» object indicating the file or directory
was last changed.

=for code
say "path/to/file".IO.changed;           # Instant:1424089165
say "path/to/file".IO.changed.DateTime;  # 2015-02-16T12:18:50Z

=head1 File permissions retrieval

=head2 method mode

Return an L<IntStr> object representing the POSIX permissions of a file.  The
Str part of the result is the octal representation of the file permission, like
the form accepted by the chmod(1) utility.

=for code
say ~"path/to/file".IO.mode;        # e.g. '0644'
say +"path/to/file".IO.mode;        # e.g. 420, where sprintf('%04o', 420) eq '0644'

The result of this can be used in the other methods that take a mode as an
argument.

=for code
"path/to/file1".IO.chmod("path/to/file2".IO.mode);  # will change the
                                                    # permissions of file1
                                                    # to be the same as file2


=end pod

# vim: expandtab shiftwidth=4 ft=perl6
